// Two-Dimensional Sierpinski Gasket       
// Generated using randomly selected vertices and bisection

//#include "Angel.h"
#include "BoundingBox.h"

//----------------------------------------------------------------------------
int width = 0;
int height = 0;

// remember to prototype
void generateGeometry( void );
void display( void );
void keyboard( unsigned char key, int x, int y );
void modelTransform();
void quad( int a, int b, int c, int d );
void colorcube(void);
void drawCube(void);
void storePLYFile(int id);
void drawPLY();
void projectionSetup();
void modelMatrixConvertion(Angel::mat4 mat);
void init();
void setBoundingBoxPoints();
void reset();
void setNormalPoints();

//typedef Angel::vec4  color4;
//typedef Angel::vec4  point4;
//typedef Angel::vec3  point3;

// handle to program
GLuint program;
GLuint modelMatrix;

using namespace std;

GLuint buffer;
const int NumVertices = 50000; 
const int NumNormalPoints = 50000;

point4 points[NumVertices];
color4 colors[NumVertices];
point4 vertices[50000];
point4 boundingBoxPoints[16];//4*4
point4 normals[NumNormalPoints];
point3 nm[NumNormalPoints/2];
GLint numOfVertices = 0;
GLint numOfPolygons = 0;
BoundingBox boundingBox;

Angel::mat4 modelMat;
float modelMatrixf[16];

string plyFileFolder = "ply_files\\\\";
string plyFileExt = ".ply";
string plyFileNames[] = {
	"airplane","ant","apple","balance","beethoven",
	"big_atc","big_dodge","big_porsche","big_spider",
	"canstick","chopper","cow","dolphins","egret",
	"f16","footbones","fracttree","galleon","hammerhead",
	"helix","hind","kerolamp","ketchup","mug",
	"part","pickup_big","pump","pumpa_tb","sandal",
	"saratoga","scissors","shark","steeringweel","stratocaster",
	"street_lamp","teapot","tennis_shoe","tommygun","trashcan",
	"turbine","urn2","walkman","weathervane"};
	const int numOfFiles = 43;
	int currentId = 11;

	//translate variables
	GLfloat moveX = 0.0f;
	GLfloat moveY = 0.0f;
	GLfloat moveZ = 0.0f;
	bool toggle_X = false;
	bool toggle_x = false;
	bool toggle_Y = false;
	bool toggle_y = false;
	bool toggle_Z = false;
	bool toggle_z = false;
	point3 positionOffset = point3(0.0f,0.0f,0.0f);//remember to reset

	//rotate variables
	GLfloat rotateDeltaY = 0.0f;
	const GLfloat rotateSpeed = 0.05f;
	GLfloat rotateY = 0.0f;
	bool toggle_R = false;
	bool clockwise = false;

	//pulsing variables
	int pulsingCount = 0;
	bool pulsingOut = true;
	//const float pulsingFactor = 0.1f;
	bool pulsingMode = false;

	//normal variables
	bool normalMode = false;

	//boundingbox variables
	bool boundingBoxMode = false;
	float boundingBoxRange = 0.0f;

	// Vertices of a unit cube centered at origin, sides aligned with axes

	/*point4 vertices[8] = {
	point4( -0.5, -0.5,  0.5, 1.0 ),
	point4( -0.5,  0.5,  0.5, 1.0 ),
	point4(  0.5,  0.5,  0.5, 1.0 ),
	point4(  0.5, -0.5,  0.5, 1.0 ),
	point4( -0.5, -0.5, -0.5, 1.0 ),
	point4( -0.5,  0.5, -0.5, 1.0 ),
	point4(  0.5,  0.5, -0.5, 1.0 ),
	point4(  0.5, -0.5, -0.5, 1.0 )
	};*/
	// RGBA olors
	color4 vertex_colors[8] = {
		color4( 0.0, 0.0, 0.0, 1.0 ),  // black
		color4( 1.0, 0.0, 0.0, 1.0 ),  // red
		color4( 1.0, 1.0, 0.0, 1.0 ),  // yellow
		color4( 0.0, 1.0, 0.0, 1.0 ),  // green
		color4( 0.0, 0.0, 1.0, 1.0 ),  // blue
		color4( 1.0, 0.0, 1.0, 1.0 ),  // magenta
		color4( 1.0, 1.0, 1.0, 1.0 ),  // white
		color4( 0.0, 1.0, 1.0, 1.0 )   // cyan
	};
	// quad generates two triangles for each face and assigns colors
	//    to the vertices
	int Index = 0;
	void quad( int a, int b, int c, int d )
	{
		colors[Index] = vertex_colors[a]; points[Index] = vertices[a]; Index++;
		colors[Index] = vertex_colors[b]; points[Index] = vertices[b]; Index++;
		colors[Index] = vertex_colors[c]; points[Index] = vertices[c]; Index++;
		colors[Index] = vertex_colors[a]; points[Index] = vertices[a]; Index++;
		colors[Index] = vertex_colors[c]; points[Index] = vertices[c]; Index++;
		colors[Index] = vertex_colors[d]; points[Index] = vertices[d]; Index++;
	}

	void triangle( int a, int b, int c)
	{
		colors[Index] = vertex_colors[2]; points[Index] = vertices[a]; Index++;
		colors[Index] = vertex_colors[3]; points[Index] = vertices[b]; Index++;
		colors[Index] = vertex_colors[4]; points[Index] = vertices[c]; Index++;

	}

	vector<std::string> split(string str,string pattern)
	{
		string::size_type pos;
		vector<std::string> result;
		str+=pattern;//add a pattern at the end
		int size=str.size();

		for(int i=0; i<size; i++)
		{
			pos=str.find(pattern,i);
			if(pos<size)
			{
				std::string s=str.substr(i,pos-i);
				result.push_back(s);
				i=pos+pattern.size()-1;
			}
		}
		return result;
	}

	// generate 12 triangles: 36 vertices and 36 colors
	void colorcube()
	{
		quad( 1, 0, 3, 2 );
		quad( 2, 3, 7, 6 );
		quad( 3, 0, 4, 7 );
		quad( 6, 5, 1, 2 );
		quad( 4, 5, 6, 7 );
		quad( 5, 4, 0, 1 );
	}

	void generateGeometry( void )
	{	
		colorcube();

		// Create a vertex array object
		GLuint vao;
		glGenVertexArrays( 1, &vao );
		glBindVertexArray( vao );

		// Create and initialize a buffer object
		//GLuint buffer;
		glGenBuffers( 1, &buffer );

		//show in display
		//bind buffer
		glBindBuffer( GL_ARRAY_BUFFER, buffer );
		glBufferData( GL_ARRAY_BUFFER, sizeof(points) + sizeof(colors),
			NULL, GL_STATIC_DRAW );
		glBufferSubData( GL_ARRAY_BUFFER, 0, sizeof(points), points );
		glBufferSubData( GL_ARRAY_BUFFER, sizeof(points), sizeof(colors), colors );


		// Load shaders and use the resulting shader program
		program = InitShader( "vshader1.glsl", "fshader1.glsl" );
		glUseProgram( program );
		// set up vertex arrays
		GLuint vPosition = glGetAttribLocation( program, "vPosition" );
		glEnableVertexAttribArray( vPosition );
		glVertexAttribPointer( vPosition, 4, GL_FLOAT, GL_FALSE, 0,
			BUFFER_OFFSET(0) );

		GLuint vColor = glGetAttribLocation( program, "vColor" ); 
		glEnableVertexAttribArray( vColor );
		glVertexAttribPointer( vColor, 4, GL_FLOAT, GL_FALSE, 0,
			BUFFER_OFFSET(sizeof(points)) );

		// sets the default color to clear screen
		glClearColor( 0.0, 0.0, 0.0, 1.0 ); // white background
		//glClearColor( 1.0, 1.0, 1.0, 1.0 );
	}

	void init()
	{
		modelMat = Angel::identity();
	}

	void storePLYFile(int id)
	{
		//clear array
		memset(points,0,sizeof(points));
		memset(colors,0,sizeof(colors));
		memset(normals,0,sizeof(normals));
		memset(nm,0,sizeof(nm));
		boundingBox.reset();
		memset(boundingBoxPoints,0,sizeof(boundingBoxPoints));
		//open file
		string fileString = (plyFileFolder+plyFileNames[id]+plyFileExt);
		const char* fileName = fileString.c_str();
		//load ply file
		fstream inStream;
		inStream.open(fileName,ios::in); //open file
		if(inStream.fail())
		{
			cout<<"file open failure!"<<endl;
			return;
		}

		//read ply file
		string line;
		vector<string> splitedString;

		//veriables for a model
		GLint numOfVertices, numOfPolygons;

		getline(inStream,line);//ply
		if(line!="Ply" && line!="ply")
		{
			return;//not present, exit
		}
		getline(inStream,line);//skip: format ascii
		getline(inStream,line);//read: element vertex #
		splitedString.clear();
		splitedString = split(line," ");
		numOfVertices = atoi(splitedString[2].c_str());

		for(int index = 0;index<3;index++)
		{
			getline(inStream,line);//skip: property*3
		}
		getline(inStream,line);//read: element face #
		splitedString.clear();
		splitedString = split(line," ");
		numOfPolygons = atoi(splitedString[2].c_str());

		getline(inStream,line);//skip: property
		getline(inStream,line);//skip: end_header

		//read vertices & calc bounding box
		for(int index = 0;index<numOfVertices;index++)
		{
			getline(inStream,line);
			splitedString.clear();
			splitedString = split(line," ");
			point4 vertex;
			vertex.x = atof(splitedString[0].c_str());
			vertex.y = atof(splitedString[1].c_str());
			vertex.z = atof(splitedString[2].c_str());
			vertex.w = 1.0f;
			vertices[index] = vertex;
			boundingBox.update(vertex);
		}
		float range = boundingBox.getRange();
		//clear Index
		Index = 0;

		//read polygons & calc normals

		for(int index1 = 0;index1<numOfPolygons;index1++)
		{
			getline(inStream,line);
			splitedString.clear();
			splitedString = split(line," ");
			GLint v[3];
			for(int index2 = 0;index2<3;index2++){
				v[index2] = atoi(splitedString[index2+1].c_str());
			}
			triangle(v[0],v[1],v[2]);

			//calc normal
			int normalIndex0 = index1*2;
			int normalIndex1 = normalIndex0+1;
			//point3 normal;
			for(int index3 = 0;index3<3;index3++)
			{
				nm[index1].x+=(vertices[v[index3]].y-vertices[v[(index3+1)%3]].y)*(vertices[v[index3]].z+vertices[v[(index3+1)%3]].z);
				nm[index1].y+=(vertices[v[index3]].z-vertices[v[(index3+1)%3]].z)*(vertices[v[index3]].x+vertices[v[(index3+1)%3]].x);
				nm[index1].z+=(vertices[v[index3]].x-vertices[v[(index3+1)%3]].x)*(vertices[v[index3]].y+vertices[v[(index3+1)%3]].y);

			}
			nm[index1] = nm[index1]/sqrt(nm[index1].x*nm[index1].x+nm[index1].y*nm[index1].y+nm[index1].z*nm[index1].z);
			//get startpoint
			normals[normalIndex0].x = (vertices[v[0]].x+vertices[v[1]].x+vertices[v[2]].x)/3.0;
			normals[normalIndex0].y = (vertices[v[0]].y+vertices[v[1]].y+vertices[v[2]].y)/3.0;
			normals[normalIndex0].z = (vertices[v[0]].z+vertices[v[1]].z+vertices[v[2]].z)/3.0;
			normals[normalIndex0].w = 1.0f;
			//get endpoint
			normals[normalIndex1].x = normals[normalIndex0].x+nm[index1].x;//*range;
			normals[normalIndex1].y = normals[normalIndex0].y+nm[index1].y;//*range;
			normals[normalIndex1].z = normals[normalIndex0].z+nm[index1].z;//*range;
			normals[normalIndex1].w = 1.0f;

		}

		inStream.close();

		//set boundingboxPoints
		boundingBoxRange = boundingBox.getRange();
		setBoundingBoxPoints();
		setNormalPoints();
	}

	void setNormalPoints()
	{
		for(int index = 0;index<numOfPolygons;index++)//could change it to store function
			{
				int normalIndex0 = index*2;
				int normalIndex1 = index*2+1;
				point4 normal = normals[normalIndex1]-normals[normalIndex0];

				//endpoints
				float range = boundingBox.getRange()*0.2f;
				normals[normalIndex1].x = normals[normalIndex0].x+normal.x*range;
				normals[normalIndex1].y = normals[normalIndex0].y+normal.y*range;
				normals[normalIndex1].z = normals[normalIndex0].z+normal.z*range;
				normals[normalIndex1].w = 1.0f;
			}
	}

	void setBoundingBoxPoints()
	{
		point3 min = boundingBox.getMin();
		point3 max = boundingBox.getMax();
		//1
		boundingBoxPoints[0] = point4(min.x,min.y,max.z,1.0f);
		boundingBoxPoints[1] = point4(max.x,min.y,max.z,1.0f);
		boundingBoxPoints[2] = point4(max.x,max.y,max.z,1.0f);
		boundingBoxPoints[3] = point4(min.x,max.y,max.z,1.0f);
		//2
		boundingBoxPoints[4] = point4(max.x,min.y,max.z,1.0f);
		boundingBoxPoints[5] = point4(max.x,min.y,min.z,1.0f);
		boundingBoxPoints[6] = point4(max.x,max.y,min.z,1.0f);
		boundingBoxPoints[7] = point4(max.x,max.y,max.z,1.0f);
		//3
		boundingBoxPoints[8] = point4(max.x,min.y,min.z,1.0f);
		boundingBoxPoints[9] = point4(min.x,min.y,min.z,1.0f);
		boundingBoxPoints[10] = point4(min.x,max.y,min.z,1.0f);
		boundingBoxPoints[11] = point4(max.x,max.y,min.z,1.0f);
		//4
		boundingBoxPoints[12] = point4(min.x,min.y,min.z,1.0f);
		boundingBoxPoints[13] = point4(min.x,min.y,max.z,1.0f);
		boundingBoxPoints[14] = point4(min.x,max.y,max.z,1.0f);
		boundingBoxPoints[15] = point4(min.x,max.y,min.z,1.0f);

	}

	void drawPLY()
	{
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );  
		//draw model
		glBindBuffer( GL_ARRAY_BUFFER, buffer );
		glBufferData( GL_ARRAY_BUFFER, sizeof(points) + sizeof(colors),
			NULL, GL_STATIC_DRAW );
		glBufferSubData( GL_ARRAY_BUFFER, 0, sizeof(points), points );
		glBufferSubData( GL_ARRAY_BUFFER, sizeof(points), sizeof(colors), colors );
		glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
		glEnable( GL_DEPTH_TEST );
		glDrawArrays( GL_TRIANGLES, 0, NumVertices );
		glDisable( GL_DEPTH_TEST ); 
		glFlush();

		//draw normal
		if(normalMode)
		{
			glBufferSubData( GL_ARRAY_BUFFER, 0, sizeof(normals), normals );
			glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
			glEnable(GL_DEPTH_TEST);
			glDrawArrays(GL_LINES,0,NumNormalPoints);
			glDisable( GL_DEPTH_TEST ); 
			glFlush();
		}

		if(boundingBoxMode)
		{
			glBufferSubData( GL_ARRAY_BUFFER, 0, sizeof(boundingBoxPoints), boundingBoxPoints );
			glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
			glEnable(GL_DEPTH_TEST);
			for(int index = 0;index<4;index++)
			{
				glDrawArrays(GL_LINE_LOOP,index*4,4);
			}
			glDisable( GL_DEPTH_TEST ); 
			glFlush();
		}

	}


	void drawCube(void)
	{
		// change to GL_FILL
		glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
		// draw functions should enable then disable the features 
		// that are specifit the themselves
		// the depth is disabled after the draw 
		// in case you need to draw overlays
		glEnable( GL_DEPTH_TEST );
		glDrawArrays( GL_TRIANGLES, 0, NumVertices );
		glDisable( GL_DEPTH_TEST ); 
	}

	//----------------------------------------------------------------------------
	// this is where the drawing should happen
	void display( void )
	{

		
		//projection setup
		projectionSetup();
		
		glutSwapBuffers();
		// you can implement your own buffers with textures
	}

	void projectionSetup()
	{
		//projection matrix
		Angel::mat4 perspectiveMat = Angel::Perspective((GLfloat)60.0, (GLfloat)width/(GLfloat)height, (GLfloat)0.1, (GLfloat) 2000.0);

		float viewMatrixf[16];
		viewMatrixf[0] = perspectiveMat[0][0];viewMatrixf[4] = perspectiveMat[0][1];
		viewMatrixf[1] = perspectiveMat[1][0];viewMatrixf[5] = perspectiveMat[1][1];
		viewMatrixf[2] = perspectiveMat[2][0];viewMatrixf[6] = perspectiveMat[2][1];
		viewMatrixf[3] = perspectiveMat[3][0];viewMatrixf[7] = perspectiveMat[3][1];

		viewMatrixf[8] = perspectiveMat[0][2];viewMatrixf[12] = perspectiveMat[0][3];
		viewMatrixf[9] = perspectiveMat[1][2];viewMatrixf[13] = perspectiveMat[1][3];
		viewMatrixf[10] = perspectiveMat[2][2];viewMatrixf[14] = perspectiveMat[2][3];
		viewMatrixf[11] = perspectiveMat[3][2];viewMatrixf[15] = perspectiveMat[3][3];

		modelMatrixConvertion(modelMat);

		// set up model view and projection matricies
		modelMatrix = glGetUniformLocationARB(program, "model_matrix");
		glUniformMatrix4fv( modelMatrix, 1, GL_FALSE, modelMatrixf );
		GLuint viewMatrix = glGetUniformLocationARB(program, "projection_matrix");
		glUniformMatrix4fv( viewMatrix, 1, GL_FALSE, viewMatrixf);

	}

	void modelMatrixConvertion(Angel::mat4 mat)
	{

		modelMatrixf[0] = mat[0][0];modelMatrixf[4] = mat[0][1];
		modelMatrixf[1] = mat[1][0];modelMatrixf[5] = mat[1][1];
		modelMatrixf[2] = mat[2][0];modelMatrixf[6] = mat[2][1];
		modelMatrixf[3] = mat[3][0];modelMatrixf[7] = mat[3][1];

		modelMatrixf[8] = mat[0][2];modelMatrixf[12] = mat[0][3];
		modelMatrixf[9] = mat[1][2];modelMatrixf[13] = mat[1][3];
		modelMatrixf[10] = mat[2][2];modelMatrixf[14] = mat[2][3];
		modelMatrixf[11] = mat[3][2];modelMatrixf[15] = mat[3][3];

	}
	//----------------------------------------------------------------------------
	void setPosition()
	{
		positionOffset.x = -boundingBox.getCenter().x;
		positionOffset.y = -boundingBox.getCenter().y;
		positionOffset.z = -boundingBox.getCenter().z;
	}

	void reset()
	{
		//translate variables
		moveX = 0.0f;
		moveY = 0.0f;
		moveZ = 0.0f;
		toggle_X = false;
		toggle_x = false;
		toggle_Y = false;
		toggle_y = false;
		toggle_Z = false;
		toggle_z = false;
		positionOffset = point3(0.0f,0.0f,0.0f);//remember to reset

		//rotate variables
		rotateDeltaY = 0.0f;

		rotateY = 0.0f;
		toggle_R = false;
		clockwise = false;

		//pulsing variables
		pulsingCount = 0;
		pulsingOut = true;
		//const float pulsingFactor = 0.1f;
		pulsingMode = false;

		//normal variables
		normalMode = false;

		//boundingbox variables
		boundingBoxMode = false;
		boundingBoxRange = 0.0f;
	}

	const float zFactor = 1.2f;
	// keyboard handler
	void keyboard( unsigned char key, int x, int y )
	{
		GLfloat range;
		switch ( key ) {
		case 033:
			exit( EXIT_SUCCESS );
			break;
		case 'W'://draw your wireframe
			glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 
			storePLYFile(currentId);
			modelMat = Angel::identity();
			modelMat = modelMat * Angel::Translate(-boundingBox.getCenter().x, -boundingBox.getCenter().y, -boundingBox.getCenter().z-(GLfloat)boundingBox.getRange()*zFactor);
			setPosition();
			//positionOffset = -boundingBox.getCenter();
			cout<<"position offset: "<<positionOffset<<endl;
			projectionSetup();
			drawPLY();
			glutSwapBuffers();
			break;
		case 'N'://draw next wireframe
			currentId = (currentId+1) % numOfFiles;
			glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 
			reset();
			storePLYFile(currentId);
			modelMat = Angel::identity();
			modelMat = modelMat * Angel::Translate(-boundingBox.getCenter().x, -boundingBox.getCenter().y, -(GLfloat)boundingBox.getRange()*zFactor);
			positionOffset.x = -boundingBox.getCenter().x;
			projectionSetup();
			drawPLY();
			glutSwapBuffers();
			break;
		case 'P'://draw previous wireframe
			currentId = (currentId-1+numOfFiles) % numOfFiles;
			glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
			reset();
			storePLYFile(currentId);
			modelMat = Angel::identity();
			modelMat = modelMat * Angel::Translate(-boundingBox.getCenter().x, -boundingBox.getCenter().y, -(GLfloat)boundingBox.getRange()*zFactor);
			positionOffset.x = -boundingBox.getCenter().x;
			positionOffset.y = -boundingBox.getCenter().y;
			positionOffset.z = -boundingBox.getCenter().z;
			projectionSetup();
			drawPLY();
			glutSwapBuffers();
			break;
		case 'X':
			if(!toggle_X){
				moveX = 0.00005f*boundingBox.getRange();
				toggle_X = true;
			}
			else{
				moveX = 0.0f;
				toggle_X = false;
			}
			break;
		case 'x':
			if(!toggle_x){
				moveX = -0.00005f*boundingBox.getRange();
				toggle_x = true;
			}
			else{
				moveX = 0.0f;
				toggle_x = false;
			}
			break;
		case 'Y':
			if(!toggle_Y){
				moveY = 0.00005f*boundingBox.getRange();
				toggle_Y = true;
			}
			else{
				moveY = 0.0f;
				toggle_Y = false;
			}
			break;
		case 'y':
			if(!toggle_y){
				moveY = -0.00005f*boundingBox.getRange();
				toggle_y = true;
			}
			else{
				moveY = 0.0f;
				toggle_y = false;
			}
			break;

		case 'Z':
			if(!toggle_Z){
				moveZ = 0.00005f*boundingBox.getRange();
				toggle_Z = true;
			}
			else{
				moveZ = 0.0f;
				toggle_Z = false;
			}
			break;
		case 'z'://translate
			if(!toggle_z){
				moveZ = -0.00005f*boundingBox.getRange();
				toggle_z = true;
			}
			else{
				moveZ = 0.0f;
				toggle_z = false;
			}
			break;

		case 'R'://Rotate about current pos
			if(!toggle_R){
				clockwise = false;
				rotateDeltaY = rotateSpeed;
				toggle_R = true;
			}
			else{
				rotateDeltaY = 0.0f;
				toggle_R = false;
			}
			break;
		case 'B'://toggle pulsing meshes
			if(pulsingMode)
			{
				pulsingMode = false;
			}
			else
			{
				pulsingMode = true;
			}

			break;
		case 'm'://toggle normal
			if(normalMode){
				normalMode = false;
			}
			else
			{
				normalMode = true;

			}
			glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 
			//storePLYFile(currentId);
			modelMat = Angel::identity();
			modelMat = modelMat * Angel::Translate(-boundingBox.getCenter().x, -boundingBox.getCenter().y, -(GLfloat)boundingBox.getRange()*zFactor);
			positionOffset.x = -boundingBox.getCenter().x;
			positionOffset.y = -boundingBox.getCenter().y;
			positionOffset.z = -boundingBox.getCenter().z;
			projectionSetup();
			drawPLY();
			glutSwapBuffers();

			break;
		case 'e'://toggle bounding box
			if(boundingBoxMode)boundingBoxMode = false;
			else boundingBoxMode = true;
			glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 
			//storePLYFile(currentId);
			modelMat = Angel::identity();
			modelMat = modelMat * Angel::Translate(-boundingBox.getCenter().x, -boundingBox.getCenter().y, -(GLfloat)boundingBox.getRange()*zFactor);
			positionOffset.x = -boundingBox.getCenter().x;
			positionOffset.y = -boundingBox.getCenter().y;
			positionOffset.z = -boundingBox.getCenter().z;
			projectionSetup();
			drawPLY();
			glutSwapBuffers();
			break;

		}
	}

	float pulsingFactor = 0.0001f;
	void modelTransform()
	{
		
		
		modelMat = modelMat*Angel::Translate(moveX,moveY,moveZ)*
			Translate(-positionOffset.x,-positionOffset.y,-positionOffset.z)*
			RotateY(rotateDeltaY)*
			Translate(positionOffset.x,positionOffset.y,positionOffset.z);
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 
		projectionSetup();
		drawPLY();
		glutSwapBuffers();
		rotateY+=abs(rotateDeltaY);
		if(rotateY>=360.0f)
		{
			rotateY = 0.0f;
			rotateDeltaY = -rotateDeltaY;

			currentId = (currentId+1) % numOfFiles;
			storePLYFile(currentId);
			modelMat = Angel::identity();
			modelMat = modelMat * Angel::Translate(-boundingBox.getCenter().x, -boundingBox.getCenter().y, -(GLfloat)boundingBox.getRange()*zFactor);
			setPosition();
			projectionSetup();
			drawPLY();
			glutSwapBuffers();

		}

//pulsing mode
		if(pulsingMode)
		{
			pulsingFactor = 0.00001f*boundingBoxRange;
			if(pulsingOut == true)
			{
				for(int index = 0;index < NumVertices;index++)
				{
					point4 delta = nm[index]*pulsingFactor;
					points[index] += delta; points[index].w = 1.0f;
					points[index+1] += delta; points[index+1].w = 1.0f;
					points[index+2] += delta; points[index+2].w = 1.0f;
				}
				pulsingCount++;
				if(pulsingCount == 100)
				{
					pulsingOut = false;
				}
			}
			else
			{
				for(int index = 0;index < NumVertices;index++)
				{
					point4 delta = -nm[index]*pulsingFactor;
					points[index] += delta; points[index].w = 1.0f;
					points[index+1] += delta; points[index+1].w = 1.0f;
					points[index+2] += delta; points[index+2].w = 1.0f;
				}
				pulsingCount--;
				if(pulsingCount == 0)
				{
					pulsingOut = true;
				}
			}
			glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 
			projectionSetup();
			drawPLY();
			glutSwapBuffers();

		}
		


	}

	//----------------------------------------------------------------------------
	// entry point
	int main( int argc, char **argv )
	{
		// init glut
		glutInit( &argc, argv );
		glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
		width = 768;
		height = 768;
		glutInitWindowSize( width, height );

		// If you are using freeglut, the next two lines will check if 
		// the code is truly 3.2. Otherwise, comment them out

		// should run a test here 
		// with different cases
		// this is a good time to find out information about
		// your graphics hardware before you allocate any memory
		glutInitContextVersion( 3, 1 );
		glutInitContextProfile( GLUT_CORE_PROFILE );

		// create window
		// opengl can be incorperated into other packages like wxwidgets, fltoolkit, etc.
		glutCreateWindow( "Color Cube" );

		// init glew
		glewInit();

		generateGeometry();
		init();
		// assign handlers
		glutDisplayFunc( display );
		glutKeyboardFunc( keyboard );
		glutIdleFunc(modelTransform);
		// should add menus
		// add mouse handler
		// add resize window functionality (should probably try to preserve aspect ratio)

		// enter the drawing loop
		// frame rate can be controlled with 
		glutMainLoop();
		return 0;
	}
